/*
 * ebcd_accept.c - EBCD Accept New Connection
 * Clunix, KyungWoon Cho, cezanne@clunix.com, 2006.2
 *
 * Copyright (c) 2001-2008 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "ebcd.h"

static void
accept_new_client(ebc_pch_t *pchp, sr_t *sr_modinfo)
{
	ebcd_pch_t	*dpch;
	sr_t	*sr_res;

	pchp->ndid = ebcd_ndid;
	pchp->name = ecm_strdup(sr_get_str(sr_modinfo));
	sr_free(sr_modinfo);

	sr_res = sr_create_dir();
	dpch = ebcd_add_pch(pchp);
	if (dpch) {
		EBCDDBG(DBG_EBCD, "%s: new mod accepted", pchp->name ? pchp->name : "anonymous");
		sr_append(sr_res, sr_create_int(ST_INT, EBC_OK));
		sr_append(sr_res, sr_create_str(ST_STR, ebcd_clname));
		sr_append(sr_res, sr_create_str(ST_STR, ebcd_ndname));
		sr_append(sr_res, sr_create_int(ST_INT, ebcd_clid));
		sr_append(sr_res, sr_create_int(ST_INT, ebcd_ndid));
		sr_append(sr_res, sr_create_int(ST_INT, pchp->mdid));
	}
	else
		sr_append(sr_res, sr_create_int(ST_INT, -ERR_EBC_EXIST));

	if (!pch_send_sr(pchp, sr_res)) {
		if (dpch)
			ebcd_del_pch(dpch);
		else
			ebc_pch_free(pchp);
	}
	else
		ebcd_accept_pch(dpch);
	sr_free(sr_res);
}

static ECMBOOL
check_conflict(const sr_t *sr_modinfo)
{
	const sr_t	*sr_ndid;

	SR_FOREACH_CHILD(sr_modinfo, "ndids", sr_ndid) {
		if (sr_get_int(sr_ndid) == ebcd_ndid)
			return ECM_TRUE;
	}
	return ECM_FALSE;
}

static void
accept_new_ebcd(ebc_pch_t *pchp, sr_t *sr_modinfo)
{
	ebcd_pch_t	*dpch = NULL;
	sr_t	*sr_res;
	ebc_err_t	err;

	sr_res = sr_create_dir();
	sr_createc_str(sr_res, "ndname", ST_STR, ebcd_ndname);

	if (check_conflict(sr_modinfo)) {
		err = -ERR_EBC_INVARG;
		goto out;
	}

	pchp->name = ecm_strdup(sr_getc_str(sr_modinfo, "ndname"));
	if (ecm_empty_str(pchp->name)) {
		err = -ERR_EBC_INVARG;
		goto out;
	}
	ebc_get_ndid(pchp->name, &pchp->ndid);

	dpch = ebcd_add_pch(pchp);
	if (dpch) {
		EBCDDBG(DBG_EBCD, "%s: new ebcd accepted", ebc_dbg_pch(pchp));
		ECMVERBOSE("%s: node accepted", pchp->name);
		err = EBC_OK;
	}
	else
		err = -ERR_EBC_EXIST;
out:
	sr_createc_int(sr_res, "err", ST_INT, err);
	if (!pch_send_sr(pchp, sr_res) || err != EBC_OK) {
		if (dpch)
			ebcd_del_pch(dpch);
		else
			ebc_pch_free(pchp);
	}
	else
		ebcd_accept_pch(dpch);
	sr_free(sr_res);
	sr_free(sr_modinfo);
}

static void
do_accept_new_connection(ecm_ptr_t transob, ECMBOOL ebcd_only)
{
	ebc_pch_t	*pchp;
	sr_t		*sr_modinfo;

	pchp = ebc_pch_new(pchp_accept->type);
	pchp->transob = transob;
	if ((sr_modinfo = pch_recv_sr(pchp, 5000)) == NULL) {
		ebc_pch_free(pchp);
		return;
	}

	if (sr_get_type(sr_modinfo) == ST_STR) {
		if (ebcd_only) {
			ebc_pch_free(pchp);
			sr_free(sr_modinfo);
		}
		else
			accept_new_client(pchp, sr_modinfo);
	}
	else
		accept_new_ebcd(pchp, sr_modinfo);
}

static void
accept_new_ebcd_connection(ecm_ptr_t arg)
{
	do_accept_new_connection(arg, ECM_TRUE);
}

static void
accept_new_connection(ecm_ptr_t arg)
{
	do_accept_new_connection(arg, ECM_FALSE);
}

void
ebcd_accept(ECMBOOL ebcd_only)
{
	ecm_ptr_t	new_transob;
	ecm_uint32	timeout = 0;

	if (!PCH_ISSET_EVENT(pchp_accept, ebcd_selection, ECM_ACCEPT_EVENT))
		return;

	if (PCH_ACCEPT(pchp_accept, &new_transob, &timeout) != EBC_OK)
		return;

	if (ebcd_only)
		ecm_start_worker(ebcd_workers, accept_new_ebcd_connection, new_transob);
	else
		ecm_start_worker(ebcd_workers, accept_new_connection, new_transob);
}
